using Unity.Entities;
using Unity.Transforms;
using Unity.Burst;
using Unity.Mathematics;
using System;
using System.Collections.Generic;
using Unity.Collections;

public struct AnimationTimeRange {
	public float StartTime;
	public float EndTime;
	public bool IsLoop;
}

[UpdateAfter(typeof(SimulatorSystem))]
[BurstCompile]
public partial struct AnimSystem : ISystem {
	public float2 playerPosition;
	private NativeList<AnimationTimeRange> animStateMap;

	public void OnCreate(ref SystemState state) {
		// playerPosition = new float2(0, 0);
		InitAnimStateMap();
	}

	[BurstCompile]
	public void OnUpdate(ref SystemState state) {
		foreach (var (_, b) in SystemAPI.Query<RefRO<PlayerTag>, RefRO<LocalTransform>>()) {
			playerPosition = b.ValueRO.Position.xz;
		}

		var animTimeJob = new AnimTimeJob {
			dt = SystemAPI.Time.DeltaTime,
			playerPosition = playerPosition,
			animStateMap = animStateMap
		};
		animTimeJob.ScheduleParallel(state.Dependency).Complete();
	}

	public void OnDestroy(ref SystemState state) { }

	[BurstCompile]
	public partial struct AnimTimeJob : IJobEntity {
		[ReadOnly] public float dt;
		[ReadOnly] public float2 playerPosition;
		[ReadOnly] public NativeList<AnimationTimeRange> animStateMap;

		private float speed;
		private float distance;
		private AnimStateType curState;
		private AnimStateType preState;

		public void Execute(Entity entity, [EntityIndexInQuery] int entityIndexInQuery, ShderAnimAspect shderAnimAspect, ref Agent agent, ref AnimState animState) {
			// shderAnimAspect.UpdateCurTime(dt);
			curState = animState.curState;
			preState = animState.preState;

			distance = math.distance(playerPosition, agent.position);
			speed = math.length(agent.velocity);

			bool isChangeState = true;

			while (isChangeState) {
				switch (curState) {
					case AnimStateType.Main:
						isChangeState = MainCheck();
						break;
					case AnimStateType.Idle:
						isChangeState = IdleCheck();
						break;
					case AnimStateType.Move:
						isChangeState = MoveCheck();
						break;
					case AnimStateType.Walk:
						isChangeState = WalkCheck();
						break;
					case AnimStateType.Run:
						isChangeState = RunCheck();
						break;
					case AnimStateType.Attack:
						isChangeState = AttackCheck();
						break;
					// case AnimStateType.Dissolve:
					// 	isChangeState = true;
					// 	break;
					default:
						isChangeState = false;
						break;
				}
			}

			if (curState != animState.curState) {
				animState.curState = curState;
				StateUpdate(curState, shderAnimAspect);
				return;
			}

			shderAnimAspect.UpdateCurTime(dt);
		}

		private bool MainCheck() {
			if (distance <= 0.2f) {
				curState = AnimStateType.Attack;
				return false;
			} else if (speed <= 0.1f) {
				curState = AnimStateType.Idle;
				return false;
			}

			curState = AnimStateType.Move;
			return true;
		}

		private bool IdleCheck() {
			if (distance <= 0.2f || speed > 0.1f) {
				curState = AnimStateType.Main;
				return true;
			}

			return false;
		}

		private bool MoveCheck() {
			if (distance <= 0.2f || speed <= 0.1f) {
				curState = AnimStateType.Main;
				return true;
			} else if (speed >= 0.7f) {
				curState = AnimStateType.Run;
			} else {
				curState = AnimStateType.Walk;
			}

			return false;
		}

		private bool WalkCheck() {
			if (distance <= 0.2f || speed <= 0.1f || speed >= 0.7f) {
				curState = AnimStateType.Move;
				return true;
			}
			return false;
		}

		private bool RunCheck() {
			if (distance <= 0.2f || speed <= 0.1f || speed < 0.7f) {
				curState = AnimStateType.Move;
				return true;
			}
			return false;
		}

		private bool AttackCheck() {
			if (distance > 0.2f) {
				curState = AnimStateType.Main;
				return true;
			}

			return false;
		}

		private void StateUpdate(AnimStateType curState, ShderAnimAspect shderAnimAspect) {
			var newStartTime = animStateMap[(int)curState].StartTime;
			var newEndTime = animStateMap[(int)curState].EndTime;
			var newIsLoop = animStateMap[(int)curState].IsLoop ? 1 : 0;

			shderAnimAspect.UpdateAnim(newStartTime, newEndTime, newIsLoop);
		}
	}

	private void InitAnimStateMap() {
		animStateMap = new NativeList<AnimationTimeRange>(Allocator.Persistent) {
			new AnimationTimeRange { StartTime = 0, EndTime = 80, IsLoop = true },
			new AnimationTimeRange { StartTime = 81, EndTime = 141, IsLoop = true },
			new AnimationTimeRange { StartTime = 142, EndTime = 202, IsLoop = true },
			new AnimationTimeRange { StartTime = 203, EndTime = 253, IsLoop = true }
		};
	}
}
